home *** CD-ROM | disk | FTP | other *** search
- /*
- Generic Convex Polygon Scan Conversion and Clipping
- by Paul Heckbert
- from "Graphics Gems", Academic Press, 1990
- */
-
- /* poly.h: definitions for polygon package */
-
- #ifndef POLY_HDR
- #define POLY_HDR
-
- #define POLY_NMAX 8 /* max #sides to a polygon; change if needed */
-
- typedef struct { /* A POLYGON VERTEX */
- double sx, sy, sz, sw; /* screen space position */
- /* (sometimes homo.) */
- double x, y, z; /* world space position */
- double u, v, q; /* texture position */
- /* (sometimes homogeneous) */
- double r, g, b; /* (red,green,blue) color */
- double nx, ny, nz; /* world space normal vector */
- } Poly_vert;
- /* update poly.c if you change this structure */
-
- typedef struct { /* A POLYGON */
- int n; /* number of sides */
- int mask; /* interpolation mask for vertex elems */
- Poly_vert vert[POLY_NMAX]; /* vertices */
- } Poly;
- /*
- * mask is an interpolation mask whose kth bit indicates whether the kth
- * double in a Poly_vert is relevant.
- * For example, if the valid attributes are sx, sy, and sz, then set
- * mask = POLY_MASK(sx) | POLY_MASK(sy) | POLY_MASK(sz);
- */
-
- typedef struct { /* A BOX (TYPICALLY IN SCREEN SPACE) */
- double x0, x1; /* left and right */
- double y0, y1; /* top and bottom */
- double z0, z1; /* near and far */
- } Poly_box;
-
-
- typedef struct { /* WINDOW: A DISCRETE 2-D RECTANGLE */
- int x0, y0; /* xmin and ymin */
- int x1, y1; /* xmax and ymax (inclusive) */
- } Window;
-
- #define POLY_MASK(elem) (1 << (&poly_dummy->elem - \
- (double *)poly_dummy))
-
- #define POLY_CLIP_OUT 0 /* polygon entirely outside box */
- #define POLY_CLIP_PARTIAL 1 /* polygon partially inside */
- #define POLY_CLIP_IN 2 /* polygon entirely inside box */
-
- extern Poly_vert *poly_dummy; /* used superficially by */
- /* POLY_MASK macro */
-
- void poly_print(/* str, p */);
- void poly_vert_label(/* str, mask */);
- void poly_vert_print(/* str, v, mask */);
- int poly_clip_to_box(/* p1, box */);
- void poly_clip_to_halfspace(/* p, q, index, sign, k, name */);
- void poly_scan(/* p, win, pixelproc */);
-
- #endif
-
-
- /*
- * poly.c: simple utilities for polygon data structures
- */
-
- #include "poly.h"
-
- Poly_vert *poly_dummy; /* used superficially by POLY_MASK macro */
-
- /*
- * poly_print: print Poly p to stdout, prefixed by the label str */
-
- void poly_print(str, p)
- char *str;
- Poly *p;
- {
- int i;
-
- printf("%s: %d sides\n", str, p->n);
- poly_vert_label(" ", p->mask);
- for (i=0; i<p->n; i++) {
- printf(" v[%d] ", i);
- poly_vert_print("", &p->vert[i], p->mask);
- }
- }
-
-
- void poly_vert_label(str, mask)
- char *str;
- int mask;
- {
- printf("%s", str);
- if (mask&POLY_MASK(sx)) printf(" sx ");
- if (mask&POLY_MASK(sy)) printf(" sy ");
- if (mask&POLY_MASK(sz)) printf(" sz ");
- if (mask&POLY_MASK(sw)) printf(" sw ");
- if (mask&POLY_MASK(x)) printf(" x ");
- if (mask&POLY_MASK(y)) printf(" y ");
- if (mask&POLY_MASK(z)) printf(" z ");
- if (mask&POLY_MASK(u)) printf(" u ");
- if (mask&POLY_MASK(v)) printf(" v ");
- if (mask&POLY_MASK(q)) printf(" q ");
- if (mask&POLY_MASK(r)) printf(" r ");
- if (mask&POLY_MASK(g)) printf(" g ");
- if (mask&POLY_MASK(b)) printf(" b ");
- if (mask&POLY_MASK(nx)) printf(" nx ");
- if (mask&POLY_MASK(ny)) printf(" ny ");
- if (mask&POLY_MASK(nz)) printf(" nz ");
- printf("\n");
- }
-
- void poly_vert_print(str, v, mask)
- char *str;
- Poly_vert *v;
- int mask;
- {
- printf("%s", str);
- if (mask&POLY_MASK(sx)) printf(" %6.1f", v->sx);
- if (mask&POLY_MASK(sy)) printf(" %6.1f", v->sy);
- if (mask&POLY_MASK(sz)) printf(" %6.2f", v->sz);
- if (mask&POLY_MASK(sw)) printf(" %6.2f", v->sw);
- if (mask&POLY_MASK(x)) printf(" %6.2f", v->x);
- if (mask&POLY_MASK(y)) printf(" %6.2f", v->y);
- if (mask&POLY_MASK(z)) printf(" %6.2f", v->z);
- if (mask&POLY_MASK(u)) printf(" %6.2f", v->u);
- if (mask&POLY_MASK(v)) printf(" %6.2f", v->v);
- if (mask&POLY_MASK(q)) printf(" %6.2f", v->q);
- if (mask&POLY_MASK(r)) printf(" %6.4f", v->r);
- if (mask&POLY_MASK(g)) printf(" %6.4f", v->g);
- if (mask&POLY_MASK(b)) printf(" %6.4f", v->b);
- if (mask&POLY_MASK(nx)) printf(" %6.3f", v->nx);
- if (mask&POLY_MASK(ny)) printf(" %6.3f", v->ny);
- if (mask&POLY_MASK(nz)) printf(" %6.3f", v->nz);
- printf("\n");
- }
-
-
- /*
- * poly_scan.c: point-sampled scan conversion of convex polygons
- *
- * Paul Heckbert 1985, Dec 1989
- */
-
- #include <stdio.h>
- #include <math.h>
- #include "poly.h"
-
- /*
- * poly_scan: Scan convert a polygon, calling pixelproc at
- * each pixel with an interpolated Poly_vert structure.
- * Polygon can be clockwise or ccw.
- * Polygon is clipped in 2-D to win, the screen space window.
- *
- * Scan conversion is done on the basis of Poly_vert fields sx and sy.
- * These two must always be interpolated, and only they have
- * special meaning to this code; any other fields are
- * blindly interpolated regardless of their semantics.
- *
- * The pixelproc subroutine takes the arguments:
- *
- * pixelproc(x, y, point)
- * int x, y;
- * Poly_vert *point;
- *
- * All the fields of point indicated by p->mask will be
- * valid inside pixelproc except sx and sy.
- * If they were computed, they would have values
- * sx=x+.5 and sy=y+.5, since sampling is done at pixel centers.
- */
-
- void poly_scan(p, win, pixelproc)
- register Poly *p; /* polygon */
- Window *win; /* 2-D screen space clipping window */
- void (*pixelproc)(); /* procedure called at each pixel */
- {
- register int i, li, ri, y, ly, ry, top, rem, mask;
- double ymin;
- Poly_vert l, r, dl, dr;
-
- if (p->n>POLY_NMAX) {
- fprintf(stderr, "poly_scan: too many vertices: %d\n", p->n);
- return;
- }
-
-
- ymin = HUGE;
- for (i=0; i<p->n; i++) /* find top vertex (y points down) */
- if (p->vert[i].sy < ymin) {
- ymin = p->vert[i].sy;
- top = i;
- }
-
- li = ri = top; /* left and right vertex indices */
- rem = p->n; /* number of vertices remaining */
- y = ceil(ymin-.5); /* current scan line */
- ly = ry = y-1; /* lower end of left & right edges */
- mask = p->mask & ~POLY_MASK(sy); /* stop interpolating screen y */
-
- while (rem>0) { /* scan in y, activating new edges on left & */ /* right as scan line passes over new vertices */
-
- while (ly<=y && rem>0) { /* advance left edge? */
- rem--;
- i = li-1; /* step ccw down left side */
- if (i<0) i = p->n-1;
- incrementalize_y(&p->vert[li], &p->vert[i], &l,
- &dl, y, mask);
- ly = floor(p->vert[i].sy+.5);
- li = i;
- }
- while (ry<=y && rem>0) { /* advance right edge? */
- rem--;
- i = ri+1; /* step cw down right edge */
- if (i>=p->n) i = 0;
- incrementalize_y(&p->vert[ri], &p->vert[i], &r,
- &dr, y, mask);
- ry = floor(p->vert[i].sy+.5);
- ri = i;
- }
-
- while (y<ly && y<ry)
- /* do scanlines till end of l or r edge */
- if (y>=win->y0 && y<=win->y1)
- if (l.sx<=r.sx) scanline(y, &l, &r, win, pixelproc, mask);
- else scanline(y, &r, &l, win, pixelproc, mask);
- y++;
- increment(&l, &dl, mask);
- increment(&r, &dr, mask);
- }
- }
- }
-
-
- /* scanline: output scanline by sampling polygon at Y=y+.5 */
-
- static scanline(y, l, r, win, pixelproc, mask)
- int y, mask;
- Poly_vert *l, *r;
- Window *win;
- void (*pixelproc)();
- {
- int x, lx, rx;
- Poly_vert p, dp;
-
- mask &= ~POLY_MASK(sx); /* stop interpolating screen x */
- lx = ceil(l->sx-.5);
- if (lx<win->x0) lx = win->x0;
- rx = floor(r->sx-.5);
- if (rx>win->x1) rx = win->x1;
- if (lx>rx) return;
- incrementalize_x(l, r, &p, &dp, lx, mask);
- for (x=lx; x<=rx; x++) { /* scan in x, generating pixels */
- (*pixelproc)(x, y, &p);
- increment(&p, &dp, mask);
- }
- }
-
- /*
- * incrementalize_y: put intersection of line Y=y+.5 with edge
- * between points p1 and p2 in p, put change with respect to y in dp
- */
-
- static incrementalize_y(p1, p2, p, dp, y, mask)
- register double *p1, *p2, *p, *dp;
- register int mask;
- int y;
- {
- double dy, frac;
-
- dy = ((Poly_vert *)p2)->sy - ((Poly_vert *)p1)->sy;
- if (dy==0.) dy = 1.;
- frac = y+.5 - ((Poly_vert *)p1)->sy;
-
- for (; mask!=0; mask>>=1, p1++, p2++, p++, dp++)
- if (mask&1) {
- *dp = (*p2-*p1)/dy;
- *p = *p1+*dp*frac;
- }
- }
-
-
- /*
- * incrementalize_x: put intersection of line X=x+.5 with
- * edge between points p1 and p2 in p,
- * put change with respect to x in dp
- */
-
- static incrementalize_x(p1, p2, p, dp, x, mask)
- register double *p1, *p2, *p, *dp;
- register int mask;
- int x;
- {
- double dx, frac;
-
- dx = ((Poly_vert *)p2)->sx - ((Poly_vert *)p1)->sx;
- if (dx==0.) dx = 1.;
- frac = x+.5 - ((Poly_vert *)p1)->sx;
-
- for (; mask!=0; mask>>=1, p1++, p2++, p++, dp++)
- if (mask&1) {
- *dp = (*p2-*p1)/dx;
- *p = *p1+*dp*frac;
- }
- }
-
- static increment(p, dp, mask)
- register double *p, *dp;
- register int mask;
- {
- for (; mask!=0; mask>>=1, p++, dp++)
- if (mask&1)
- *p += *dp;
- }
-
-
- /*
- * poly_clip.c: homogeneous 3-D convex polygon clipper
- *
- * Paul Heckbert 1985, Dec 1989
- */
-
- #include "poly.h"
-
- #define SWAP(a, b, temp) {temp = a; a = b; b = temp;}
- #define COORD(vert, i) ((double *)(vert))[i]
-
- #define CLIP_AND_SWAP(elem, sign, k, p, q, r) { \
- poly_clip_to_halfspace(p, q, &v->elem-(double *)v, sign, sign*k); \
- if (q->n==0) {p1->n = 0; return POLY_CLIP_OUT;} \
- SWAP(p, q, r); \
- }
-
-
- /*
- * poly_clip_to_box: Clip the convex polygon p1 to the screen space box
- * using the homogeneous screen coordinates (sx, sy, sz, sw) of each
- * vertex, testing if v->sx/v->sw > box->x0 and v->sx/v->sw < box->x1,
- * and similar tests for y and z, for each vertex v of the polygon.
- * If polygon is entirely inside box, then POLY_CLIP_IN is returned.
- * If polygon is entirely outside box, then POLY_CLIP_OUT is returned.
- * Otherwise, if the polygon is cut by the box, p1 is modified and
- * POLY_CLIP_PARTIAL is returned.
- */
-
- int poly_clip_to_box(p1, box)
- register Poly *p1;
- register Poly_box *box;
- {
- int x0out = 0, x1out = 0, y0out = 0, y1out = 0, z0out = 0,
- z1out = 0;
- register int i;
- register Poly_vert *v;
- Poly p2, *p, *q, *r;
-
- /* count vertices "outside" with respect to each */
- /* of the six planes */
- for (v=p1->vert, i=p1->n; i>0; i--, v++) {
- if (v->sx < box->x0*v->sw) x0out++; /* out on left */
- if (v->sx > box->x1*v->sw) x1out++; /* out on right */
- if (v->sy < box->y0*v->sw) y0out++; /* out on top */
- if (v->sy > box->y1*v->sw) y1out++; /* out on bottom */
- if (v->sz < box->z0*v->sw) z0out++; /* out on near */
- if (v->sz > box->z1*v->sw) z1out++; /* out on far */
- }
-
- /* check if all vertices inside */
- if (x0out+x1out+y0out+y1out+z0out+z1out == 0) return POLY_CLIP_IN;
-
- /* check if all vertices are "outside" any of the six planes */
- if (x0out==p1->n || x1out==p1->n || y0out==p1->n ||
- y1out==p1->n || z0out==p1->n || z1out==p1->n) {
- p1->n = 0;
- return POLY_CLIP_OUT;
- }
-
-
- /*
- * now clip against each of the planes that might cut the polygon,
- * at each step toggling between polygons p1 and p2
- */
- p = p1;
- q = &p2;
- if (x0out) CLIP_AND_SWAP(sx, -1., box->x0, p, q, r);
- if (x1out) CLIP_AND_SWAP(sx, 1., box->x1, p, q, r);
- if (y0out) CLIP_AND_SWAP(sy, -1., box->y0, p, q, r);
- if (y1out) CLIP_AND_SWAP(sy, 1., box->y1, p, q, r);
- if (z0out) CLIP_AND_SWAP(sz, -1., box->z0, p, q, r);
- if (z1out) CLIP_AND_SWAP(sz, 1., box->z1, p, q, r);
-
- /* if result ended up in p2 then copy it to p1 */
- if (p==&p2)
- bcopy(&p2, p1, sizeof(Poly)-
- (POLY_NMAX-p2.n)*sizeof(Poly_vert));
- return POLY_CLIP_PARTIAL;
- }
-
- /*
- * poly_clip_to_halfspace: clip convex polygon p against a plane,
- * copying the portion satisfying sign*s[index] < k*sw into q,
- * where s is a Poly_vert* cast as a double*.
- * index is an index into the array of doubles at each vertex, such that
- * s[index] is sx, sy, or sz (screen space x, y, or z).
- * Thus, to clip against xmin, use
- * poly_clip_to_halfspace(p, q, XINDEX, -1., -xmin);
- * and to clip against xmax, use
- * poly_clip_to_halfspace(p, q, XINDEX, 1., xmax);
- */
-
- void poly_clip_to_halfspace(p, q, index, sign, k)
- Poly *p, *q;
- register int index;
- double sign, k;
- {
- register int m;
- register double *up, *vp, *wp;
- register Poly_vert *v;
- int i;
- Poly_vert *u;
- double t, tu, tv;
-
- q->n = 0;
- q->mask = p->mask;
-
-
- /* start with u=vert[n-1], v=vert[0] */
- u = &p->vert[p->n-1];
- tu = sign*COORD(u, index) - u->sw*k;
- for (v= &p->vert[0], i=p->n; i>0; i--, u=v, tu=tv, v++) {
- /* on old polygon (p), u is previous vertex, v is current vertex */
- /* tv is negative if vertex v is in */
- tv = sign*COORD(v, index) - v->sw*k;
- if (tu<=0. ^ tv<=0.) {
- /* edge crosses plane; add intersection point to q */
- t = tu/(tu-tv);
- vp = (double *)v;
- wp = (double *)&q->vert[q->n];
- for (m=p->mask; m!=0; m>>=1, up++, vp++, wp++)
- if (m&1) *wp = *up+t*(*vp-*up);
- q->n++;
- }
- if (tv<=0.) /* vertex v is in, copy it to q */
- q->vert[q->n++] = *v;
- }
- }
-
-
- /*
- * scantest.c: use poly_scan() for Gouraud shading and z-buffer demo.
- * Given the screen space X, Y, and Z of N-gon on command line,
- * print out all pixels during scan conversion.
- * This code could easily be modified to actually read and write pixels.
- *
- * Paul Heckbert Dec 1989
- */
-
- #include <stdio.h>
- #include <math.h>
- #include "poly.h"
-
- #define XMAX 1280 /* hypothetical image width */
- #define YMAX 1024 /* hypothetical image height */
-
- #define FRANDOM() ((rand()&32767)/32767.)
- /* random number between 0 and 1 */
-
- void pixelproc();
-
-
- main(ac, av)
- int ac;
- char **av;
- {
- int i;
- Poly p;
- static Window win = {0, 0, XMAX-1, YMAX-1};
- /* screen clipping window */
-
- if (ac<2 || ac != 2+3*(p.n = atoi(av[1]))) {
- fprintf(stderr,
- "Usage: scantest N X1 Y1 Z1 X2 Y2 Z2 ... XN YN ZN\n");
- exit(1);
- }
- for (i=0; i<p.n; i++) {
- p.vert[i].sx = atof(av[2+3*i]); /* set screen space x,y,z */
- p.vert[i].sy = atof(av[3+3*i]);
- p.vert[i].sz = atof(av[4+3*i]);
- p.vert[i].r = FRANDOM(); /* random vertex colors, for kicks */
- p.vert[i].g = FRANDOM();
- p.vert[i].b = FRANDOM();
- }
- /* interpolate sx, sy, sz, r, g, and b in poly_scan */
- p.mask = POLY_MASK(sx) | POLY_MASK(sy) | POLY_MASK(sz) |
- POLY_MASK(r) | POLY_MASK(g) | POLY_MASK(b);
-
- poly_print("scan converting the polygon", &p);
-
- poly_scan(&p, &win, pixelproc); /* scan convert! */
- }
-
- static void pixelproc(x, y, point)
- /* called at each pixel by poly_scan */
- int x, y;
- Poly_vert *point;
- {
- printf("pixel (%d,%d) screenz=%g rgb=(%g,%g,%g)\n",
- x, y, point->sz, point->r, point->g, point->b);
-
- /*
- * in real graphics program you could read and write pixels, e.g.:
- *
- * if (point->sz < zbuffer_read(x, y)) {
- * image_write_rgb(x, y, point->r, point->g, point->b);
- * zbuffer_write(x, y, point->sz);
- * }
- */
- }
-
- /*
- * fancytest.c: subroutine illustrating the use of poly_clip and poly_scan
- * for Phong-shading and texture mapping.
- * Note: lines enclosed in angle brackets "<", ">" should be
- * replaced with the code described.
- * Makes calls to hypothetical packages "shade", "image", "texture",
- * "zbuffer".
- * Paul Heckbert Dec 1989
- */
-
- #include <stdio.h>
- #include <math.h>
- #include "poly.h"
-
- #define XMAX 1280 /* hypothetical image width */
- #define YMAX 1024 /* hypothetical image height */
- #define LIGHT_INTEN 255 /* light source intensity */
-
- void pixelproc();
-
- fancytest()
- {
- int i;
- double WS[4][4]; /* world space to screen space transform */
- Poly p; /* a polygon */
- Poly_vert *v;
-
- static Poly_box box = {0, XMAX-1, 0, YMAX-1, -32678, 32767};
- /* 3-D screen clipping box */
-
- static Window win = {0, 0, XMAX-1, YMAX-1};
- /* 2-D screen clipping window */
-
- <initialize world space position (x,y,z), normal (nx,ny,nz), and texture position (u,v) at each vertex of p; set p.n>
- <set WS to world-to-screen transform>
-
- /* transform vertices from world space to homogeneous */
- /* screen space */
- for (i=0; i<p.n; i++) {
- v = &p.vert[i];
- mx4_transform(v->x, v->y, v->z, 1.,
- WS, &v->sx, &v->sy, &v->sz, &v->sw);
- }
-
-
- /* interpolate sx, sy, sz, sw, nx, ny, nz, u, v in poly_clip */
- p.mask = POLY_MASK(sx) | POLY_MASK(sy) | POLY_MASK(sz) | POLY_MASK(sw) | POLY_MASK(nx) | POLY_MASK(ny) | POLY_MASK(nz) |
- POLY_MASK(u) | POLY_MASK(v);
-
- poly_print("before clipping", &p);
- if (poly_clip_to_box(&p, &box) == POLY_CLIP_OUT) /* clip polygon */
- return; /* quit if off-screen */
-
- /* do homogeneous division of screen position, texture position */
- for (i=0; i<p.n; i++) {
- v = &p.vert[i];
- v->sx /= v->sw;
- v->sy /= v->sw;
- v->sz /= v->sw;
- v->u /= v->sw;
- v->v /= v->sw;
- v->q = 1./v->sw;
- }
- /*
- * previously we ignored q (assumed q=1),
- * henceforth ignore sw (assume sw=1)
- * Interpolate sx, sy, sz, nx, ny, nz, u, v, q in poly_scan
- */
- p.mask &= ~POLY_MASK(sw);
- p.mask |= POLY_MASK(q);
-
- poly_print("scan converting the polygon", &p);
- poly_scan(&p, &win, pixelproc); /* scan convert! */
- }
-
- static void pixelproc(x, y, pt)
- /* called at each pixel by poly_scan */
- int x, y;
- Poly_vert *pt;
- {
- int sz, u, v, inten;
- double len, nor[3], diff, spec;
-
-
- sz = pt->sz;
- if (sz < zbuffer_read(x, y)) {
- len = sqrt(pt->nx*pt->nx + pt->ny*pt->ny + pt->nz*pt->nz);
- nor[0] = pt->nx/len; /* unitize the normal vector */
- nor[1] = pt->ny/len;
- nor[2] = pt->nz/len;
- shade(nor, &diff, &spec);
- /* compute specular and diffuse coeffs*/
- u = pt->u/pt->q; /* do homogeneous div. of texture pos */
- v = pt->v/pt->q;
- inten = texture_read(u, v)*diff + LIGHT_INTEN*spec;
- image_write(x, y, inten);
- zbuffer_write(x, y, sz);
- }
- }
-
- /* mx4_transform: transform 4-vector p by matrix m */
- /* yielding q: q = p*m */
-
- mx4_transform(px, py, pz, pw, m, qxp, qyp, qzp, qwp)
- double px, py, pz, pw, m[4][4], *qxp, *qyp, *qzp, *qwp;
- {
- *qxp = px*m[0][0] + py*m[1][0] + pz*m[2][0] + pw*m[3][0];
- *qyp = px*m[0][1] + py*m[1][1] + pz*m[2][1] + pw*m[3][1];
- *qzp = px*m[0][2] + py*m[1][2] + pz*m[2][2] + pw*m[3][2];
- *qwp = px*m[0][3] + py*m[1][3] + pz*m[2][3] + pw*m[3][3];
- }
-
-
-
-
-
-